/**
 * 
 */
package org.swing.utility.system.process;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Map;
import org.swing.utility.system.conts.ProcessOutput;
import org.swing.utility.system.log.ErrorReader;
import org.swing.utility.system.stream.StreamUtils;
import org.swing.utility.system.util.StringUtil;

/**
 * @author lqnhu
 *
 */
public class ProcessUtil {
	public static boolean launch(String command) {
		try {
			String sOS = System.getProperty("os.name").toLowerCase();
			if (sOS.indexOf("window") >= 0) {
				String[] msCommand = { "cmd.exe", "/c", command };
				Runtime.getRuntime().exec(msCommand);
			} else if (sOS.indexOf("window") < 0) {
				// String[] msCommandLine = { "sh", "-c", command};
				String sCommandLine = command;
				Runtime.getRuntime().exec(sCommandLine);
			}
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	public static boolean launchAndWaitForFinish(String command) {
		try {
			String sOS = System.getProperty("os.name").toLowerCase();
			Process proc = null;
			if (sOS.indexOf("window") >= 0) {
				String[] msCommandLine = { "cmd.exe", "/c", command };
				proc = Runtime.getRuntime().exec(msCommandLine);
			} else if (sOS.indexOf("window") < 0) {
				// String[] msCommandLine = {"sh", "-c", command};
				String sCommandLine = command;
				proc = Runtime.getRuntime().exec(sCommandLine);
			}
			BufferedReader br = new BufferedReader(new InputStreamReader(
					proc.getInputStream()));
			while (br.readLine() != null)
				;
			proc.waitFor();
			proc.destroy();
			br.close();
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	public static boolean launchAndWaitForFinish(String command, String[] envp,
			File dir) {
		// Try to launch a command line program such as notepad
		try {
			String sOS = System.getProperty("os.name").toLowerCase();
			Process proc = null;
			if (sOS.indexOf("window") >= 0) {
				String[] msCommandLine = { "cmd.exe", "/c", command };
				proc = Runtime.getRuntime().exec(msCommandLine, envp, dir);
			} else if (sOS.indexOf("window") < 0) {
				String[] msCommandLine = { "sh", "-c", command };
				proc = Runtime.getRuntime().exec(msCommandLine, envp, dir);
			}
			BufferedReader br = new BufferedReader(new InputStreamReader(
					proc.getInputStream()));
			while (br.readLine() != null)
				;
			proc.waitFor();
			proc.destroy();
			br.close();
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	public static boolean killProcessByName(String processName) {
		String sOS = System.getProperty("os.name").toLowerCase();
		if (sOS.indexOf("indow") >= 0) {
			String[] cmdAttribs = new String[] { "taskkill", "/F", "/T", "/IM",
					processName };
			String sModifiedProcessName = "";
			if (processName.endsWith(".exe")) {
				sModifiedProcessName = processName.substring(0,
						processName.length() - 4);
			} else {
				sModifiedProcessName = processName + ".exe";
			}
			String[] cmdAttribs2 = new String[] { "taskkill", "/F", "/T",
					"/IM", sModifiedProcessName };
			try {
				Runtime.getRuntime().exec(cmdAttribs);
				Runtime.getRuntime().exec(cmdAttribs2);
				return true;
			} catch (IOException e) {
				return false;
			}
		} else {// "nix systems"
			String[] cmdAttribs = new String[] { "killall", "-9", processName };
			try {
				Runtime.getRuntime().exec(cmdAttribs);
				return true;
			} catch (IOException e) {
				return false;
			}
		}
	}

	public static boolean killProcessByID(String processID) {// TODO
		String sOS = System.getProperty("os.name").toLowerCase();
		if (sOS.indexOf("indow") >= 0) {
			String[] cmdAttribs = new String[] { "taskkill", "/PID", processID };
			try {
				Runtime.getRuntime().exec(cmdAttribs);
				return true;
			} catch (IOException e) {
				return false;
			}
		} else {// "nix systems"
			String[] cmdAttribs = new String[] { "kill", "-9", processID };
			try {
				Runtime.getRuntime().exec(cmdAttribs);
				return true;
			} catch (IOException e) {
				return false;
			}
		}
	}

	public static double getFreeMemory() {
		double dFreeMemory = Runtime.getRuntime().freeMemory();
		return dFreeMemory;
	}

	/*
	 * see
	 * https://weblogs.java.net/blog/emcmanus/archive/2007/08/combining_casca
	 * .html
	 */
	private static volatile Field pidField;
	private static final String PROP_LOCAL_CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress";
	private static final String PROP_JAVA_HOME = "java.home";
	private static final Path MANAGEMENT_AGENT = Paths.get("lib",
			"management-agent.jar");

	/**
	 * Returns the process ID on UNIX machines (fails on Windows).
	 */
	public static int getPid(Process process) {
		if (!process.getClass().getName().equals("java.lang.UNIXProcess"))
			throw new UnsupportedOperationException(
					"This operation is only supported in POSIX environments (Linux/Unix/MacOS");
		if (pidField == null) { // benign race
			try {
				Field f = process.getClass().getDeclaredField("pid");
				f.setAccessible(true);
				pidField = f;
			} catch (NoSuchFieldException e) {
				throw new AssertionError(e);
			} catch (SecurityException e) {
				throw new RuntimeException(e);
			}
		}
		try {
			return pidField.getInt(process);
		} catch (IllegalAccessException e) {
			throw new AssertionError(e);
		}
	}

	public static Process exec(String command[], File dir,Map<String, String> defaultEnv) throws IOException {
		Process process;

		ArrayList<String> env = new ArrayList<String>();
		{
			for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
				if (entry.getKey().equalsIgnoreCase("PWD")) {
					env.add(entry.getKey() + "=" + dir.getAbsolutePath());
				} else {
					env.add(entry.getKey() + "=" + entry.getValue());
				}
			}

			if (defaultEnv != null) {
				for (Map.Entry<String, String> entry : defaultEnv.entrySet()) {
					if (System.getenv().get(entry.getKey()) != null)
						continue;
					env.add(entry.getKey() + "=" + entry.getValue());
				}
			}
		}
		String[] envArray = new String[env.size()];
		env.toArray(envArray);

		process = dir != null ? Runtime.getRuntime().exec(command, envArray,dir) : Runtime.getRuntime().exec(command, envArray);

		return process;
	}
	
	public static Process exec(String command[], File dir) throws IOException {
		return exec(command, dir, null);
	}
	

	public static ProcessOutput execAndWait(String command[], File dir)throws IOException, InterruptedException {
		Process process = exec(command, dir);

		// empty the error stream to prevent blocking
		ErrorReader errorReader = new ErrorReader(process.getErrorStream());
		errorReader.start();

		String stdout = StreamUtils.readInputStream(process.getInputStream());
		errorReader.waitFor();
		String stderr = errorReader.getError();

		int returnCode = process.waitFor();

		return new ProcessOutput(returnCode, stdout, stderr);
	}
	
	public static Process exec(String command, File dir) throws IOException {
		ArrayList<String> list = StringUtil.tokenize(command);
		String[] array = new String[list.size()];
		list.toArray(array);

		return exec(array, dir);
	}

}
